/*****************************************************************************
 * Copyright (c) 2010, 2014 CEA LIST and others.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *  Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation
 *  Christian W. Damus (CEA) - bug 410346
 */
package org.eclipse.papyrus.uml.diagram.common.ui.dialogs;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IDisposable;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ParameterableElement;

/**
 * this classs is used to display a ui in order to look for a kind of element, with a treeviewer
 *
 */
public class ElementChooseDialog extends AbstractChooseElement {

	/** The adapter factory. */
	protected AdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);

	/** The tree viewer. */
	protected TreeViewer treeViewer;
	/** the root element */
	protected Element context;
	/** the selected element **/
	protected Element selectedElement = null;
	/** the kind of element that we do not want to select **/
	protected List<EClass> notWanted = new ArrayList<EClass>();

	/** the kind of element that we look for **/
	private EClass elementtype;

	/**
	 *
	 * @return the selected element
	 */
	public Element getSelectedElement() {
		return selectedElement;
	}

	/**
	 *
	 * create the dialog box
	 *
	 * @param parent
	 *            the parent shell
	 * @param style
	 *            the style of the dialog box
	 * @param context
	 *            the root element where we look for
	 * @param elementtype
	 *            the kind of element that we look for
	 */
	public ElementChooseDialog(Shell parent, int style, Element context, EClass elementtype) {
		super(parent, style);
		this.context = context;
		this.elementtype = elementtype;
		this.notWanted = new ArrayList<EClass>();
	}

	/**
	 *
	 * Create the dialobox
	 *
	 * @param parent
	 *            the parent shell
	 * @param style
	 *            the style of the dialog box
	 * @param context
	 *            the root element where we look for
	 * @param elementtype
	 *            the kind of element that we look for
	 * @param notWanted
	 *            the list of element that we do not want to select (list of subclasses of elementtype) cannot be null
	 */
	public ElementChooseDialog(Shell parent, int style, Element context, EClass elementtype, List<EClass> notWanted) {
		super(parent, style);
		this.context = context;
		this.elementtype = elementtype;
		assert (notWanted != null);
		this.notWanted = notWanted;
	}

	@Override
	protected void createContents() {

		super.createContents();
		// intall tree with uml element
		treeViewer = filtercontrol.getViewer();
		treeViewer.setUseHashlookup(true);

		// inner class of filter.
		// display a parent if its childreen can be choosen
		treeViewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));
		ViewerFilter filter = new ViewerFilter() {

			@Override
			public boolean select(Viewer viewer, Object parentElement, Object element) {
				if (element instanceof EObject) {// it contains nothing
					EObject eObject = (EObject) element;
					if (eObject.eContents().size() == 0) {
						return isSelectableEobject(eObject);
					} else { // it contains something so we have to test children before
						boolean result = false;
						if (isSelectableEobject(eObject)) {
							return true;
						}

						Iterator<EObject> iter = eObject.eAllContents();
						while (iter.hasNext()) {
							EObject subEObject = iter.next();
							boolean contains = select(viewer, eObject, subEObject);
							result = result || contains;
						}
						return result;


					}
				}
				return false;
			}
		};
		ViewerFilter[] filters = { filter };
		treeViewer.setFilters(filters);

		treeViewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
		treeViewer.setInput(context);


		treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				Object selection = ((IStructuredSelection) filtercontrol.getViewer().getSelection()).getFirstElement();
				if (selection instanceof EObject) {
					if (isSelectableEobject(((EObject) selection))) {
						btnSelect.setEnabled(true);
						result = selection;
						selectedElement = (ParameterableElement) selection;
					} else {
						btnSelect.setEnabled(false);
						result = null;
						selectedElement = null;
					}
				}

			}
		});
		btnSelect.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				getParent().close();
			}
		});

		btnCancel.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				result = null;
				selectedElement = null;
				getParent().close();
			}
		});

		getParent().addDisposeListener(new DisposeListener() {

			@Override
			public void widgetDisposed(DisposeEvent e) {
				// Remove the adapters created by this factory
				if (adapterFactory instanceof IDisposable) {
					((IDisposable) adapterFactory).dispose();
				}
			}
		});
	}

	/**
	 *
	 * @param eObject
	 *            that we want to test
	 * @return true if the eobject can be choosen, ie this is an instance of elementType and not instance of all type contained into notWanted
	 */
	protected boolean isSelectableEobject(EObject eObject) {
		if (elementtype.isSuperTypeOf(eObject.eClass())) {
			if (notWanted.size() > 0) {
				Iterator<EClass> iternotwanted = notWanted.iterator();
				while (iternotwanted.hasNext()) {
					if (iternotwanted.next().isSuperTypeOf(eObject.eClass())) {
						return false;
					}
				}
			}
			return true;
		}
		return false;
	}
}
